package com.example.demo.service.impl;

import com.alibaba.fastjson.JSON;
import com.example.demo.dao.CityMapper;
import com.example.demo.dao.DzdpRankMapper;
import com.example.demo.dao.DzdpSkuMapper;
import com.example.demo.domain.*;
import com.example.demo.dto.DZDPShop;
import com.example.demo.dto.DZDPShopInfo;
import com.example.demo.service.DZDPScratchService;
import com.vdurmont.emoji.EmojiParser;
import com.xiaoleilu.hutool.util.BeanUtil;
import com.xiaoleilu.hutool.util.CollectionUtil;
import org.jsoup.HttpStatusException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;


/**
 * @author ZhengWei
 * @Description
 * @date 2020/6/28 1:44 下午
 */
@Component
@Transactional
public class DZDPScratchImpl implements DZDPScratchService {
    @Autowired
    private DzdpRankMapper rankMapper;
    @Autowired
    private DzdpSkuMapper skuMapper;
    @Autowired
    private CityMapper cityMapper;

    //存放新爬到的 sku 信息
    public static ConcurrentHashMap<Integer, DzdpSku> skuMap = new ConcurrentHashMap<>(5000);

    //存放被禁请求的六位数 skuId,
    public static CopyOnWriteArraySet<Integer> forbiddenSkuIDSet = new CopyOnWriteArraySet<>();

    //sku 完整编号的第三位数字
    public static int skuIdPrefix = 1;

    //存放新爬到的 city 信息
    public static ConcurrentHashMap<Integer, String> cityMap = new ConcurrentHashMap<>(500);

    /**
     * 获取 html 数据
     */
    public Document connect(Integer cityId, Integer skuId) throws IOException {
        String url = "http://m.dianping.com/dishes/list/c" + cityId + "d" + skuId + "?notitlebar=1";
        Document document = Jsoup.connect(url).header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9").header("Accept-Encoding", "gzip, deflate").header("Cache-control", "max-age=0").header("Connection", "keep-alive").header("Host", "m.dianping.com").header("Upgrade-Insecure-Requests", "1").header("Cookie", "_hc.v=5c36ed35-fb6b-e01b-3f81-17ad2acd5bf1.1594621415; _lxsdk_cuid=17346d7cfa9c8-07dd2226e8a0be-31617402-13c680-17346d7cfa9c8; _lxsdk=17346d7cfa9c8-07dd2226e8a0be-31617402-13c680-17346d7cfa9c8; fspop=test; s_ViewType=10; wxappmain_ab=shopdish%3AA%3A1; switchcityflashtoast=1; dp_pwa_v_=fe25b31c1e622e79b2c551c1b73fa76ba19c48e7; m_flash2=1; seouser_ab=shop%3AA%3A1; logan_custom_report=; source=m_browser_test_22; Hm_lvt_233c7ef5b9b2d3d59090b5fc510a19ce=1594693704,1594693707,1594693712,1594693734; cityid=1; pvhistory=\"6L+U5ZuePjo8L3N1Z2dlc3QvZ2V0SnNvbkRhdGE/Y2FsbGJhY2s9anNvbnBfMTU5NDY5ODA3ODk1Ml8yMDUyPjo8MTU5NDY5ODA3ODk3NF1fWw==\"; _lx_utm=utm_source%3Dbing%26utm_medium%3Dorganic; Hm_lvt_602b80cf8079ae6591966cc70a3940e7=1594640491,1594693703,1594707285,1594721615; cy=1; cye=shanghai; Hm_lpvt_602b80cf8079ae6591966cc70a3940e7=1594722372; Hm_lpvt_233c7ef5b9b2d3d59090b5fc510a19ce=1594812698; msource=default; logan_session_token=4ojwx2cjo7umy1yj5xgu; default_ab=citylist%3AA%3A1%7Cshop%3AA%3A6%7Cindex%3AA%3A3%7Cspudishlist%3AA%3A1%7CshopList%3AC%3A5; _lxsdk_s=17356736fa0-1be-06f-9bf%7C%7C2").userAgent("Mozilla").get();
        return document;
    }

    @Override
    public void scratchSku(Integer cityId, Integer skuId) {
        skuId = getFormattedSkuId(skuId);
        try {
            this.doScratchSku(cityId, skuId);
        } catch (IOException e) {
            HttpStatusException e1 = (HttpStatusException) e;
            if (e1.getStatusCode() == 403) {
                System.out.print("禁止访问\t");
                DZDPScratchImpl.forbiddenSkuIDSet.add(skuId);
            } else if (e1.getStatusCode() == 404) {
                // System.out.print("菜品不存在，或在 id 为 " + cityId + " 的城市中没有id为 "+ i +" 的菜品\t");
            } else {
                System.out.print(e.getMessage() + "\t");
            }
        }
    }

    @Override
    public void doScratchSku(Integer cityId, Integer skuId) throws IOException {
        Integer skuIdFull = getFormattedSkuId(skuId);
        System.out.print("菜品 id: " + skuIdFull + "\t");

        String html = this.connect(cityId, skuIdFull).html();

        //获取菜品名称
        int beginIndex = html.indexOf(skuIdFull + ",\"name\":") + 17;
        if (beginIndex == 16) return;
        DzdpSku dzdpSku = new DzdpSku();
        String name = html.substring(beginIndex, html.indexOf("\"", beginIndex));

        String des = getSkuDescription(html, beginIndex);
        String tags = getSkuTag(html, beginIndex);

        dzdpSku.setSkuid(skuIdFull);
        dzdpSku.setSkuname(name);
        dzdpSku.setDescription(des);
        dzdpSku.setTags(tags);
        skuMap.put(skuIdFull, dzdpSku);
    }

    public Integer getFormattedSkuId(Integer skuId) {
        Integer skuIdFull = skuId;
        //如果 skuId 不是通过手动输入
        if (skuId < 10000000) {
            String s = ("000000" + skuId);
            String skuIdStr = "10"  + s.substring(s.length() - 6);
            skuIdFull = Integer.valueOf(skuIdStr);
        }
        return skuIdFull;
    }

    private String getSkuTag(String html, int beginIndex) {
        int tagIndex = html.indexOf("skuTags", beginIndex) + 10;
        String tags = "";
        if (tagIndex != 9) {
            tags = html.substring(tagIndex, html.indexOf("}", tagIndex));
            int left = tags.indexOf("[");
            int right = tags.indexOf("]") + 1;
            StringBuilder stringBuilder = new StringBuilder();
            while (left != -1 && right != -1 && left < tags.length() && right < tags.length()) {
                if (right - left > 2) stringBuilder.append(tags, left, right);
                left = tags.indexOf("[", right);
                right = tags.indexOf("]", left) + 1;
            }
            tags = stringBuilder.toString().replaceAll("\"", "");
        }
        return tags;
    }

    private String getSkuDescription(String html, int beginIndex) {
        int desIndex = html.indexOf("description", beginIndex) + 14;
        String des = "";
        if (desIndex != 13) {
            des = html.substring(desIndex, html.indexOf("\"", desIndex));
            if (des.contains("ull")) {
                des = "";
            }
        }
        return des;
    }

    @Override
    public void getCity(Integer cityId, Integer skuId) throws IOException {
        Document connect = connect(cityId, skuId);
        String html = connect.html();
        int beginIndex = html.indexOf("\"cityName\":\"") + 12;
        if (beginIndex == 11) {
            return;
        }
        String name = html.substring(beginIndex, html.indexOf("\"", beginIndex));
        System.out.println("cityId: " + cityId + ";城市名称：" + name);
        cityMap.put(cityId, name);
    }

    @Override
    public void scratchRank(Integer cityId, Integer skuId) {
        CityExample cityExample = new CityExample();
        cityExample.createCriteria().andCityidEqualTo(cityId);
        String cityName = cityMapper.selectByExample(cityExample).get(0).getCityname();

        DzdpSkuExample skuExample = new DzdpSkuExample();
        skuExample.createCriteria().andSkuidEqualTo(skuId);
        List<DzdpSku> dzdpSkus = skuMapper.selectByExample(skuExample);
        String skuName;
        if (CollectionUtil.isNotEmpty(dzdpSkus)) {
            skuName = dzdpSkus.get(0).getSkuname();
        } else {
            /**
             * 如果 sku 表中没该 skuId 对应的数据
             * 先写入 sku 表，再获取 skuname
             */
            try {
                doScratchSku(cityId, skuId);
            } catch (IOException e) {
                e.printStackTrace();
            }
            DzdpSku dzdpSku = skuMap.get(skuId);
            skuMapper.insertSelective(dzdpSku);
            skuName = dzdpSku.getSkuname();
        }

        try {
            doScratchRank(cityId, cityName, skuId, skuName);
        } catch (IOException e) {
            HttpStatusException e1 = (HttpStatusException) e;
            if (e1.getStatusCode() == 403) {
                System.out.println("禁止访问");
            } else {
                System.out.println(cityName + " 没有 " + skuName + " 榜单");
            }
        }
        System.out.println("榜单数据抓取完成");
    }

    @Override
    public void doScratchRank(Integer cityId, String cityName, Integer skuId, String skuName) throws IOException {
        List<DZDPShop> shopList = getShopList(cityId, skuId);

        //删除旧的排行数据
        DzdpRankExample rankExample = new DzdpRankExample();
        rankExample.createCriteria().andCityidEqualTo(cityId).andSkuidEqualTo(skuId);
        rankMapper.deleteByExample(rankExample);

        writeToDB(shopList, cityId, cityName, skuId, skuName);
    }

    public List<DZDPShop> getShopList(Integer cityId, Integer skuId) throws IOException {
        String html = connect(cityId, skuId).html();
        int beginIndex = html.indexOf("\"shoplist\":[") + 11;
        int endIndex = html.indexOf("},\"useNewStar\":");
        if (beginIndex == 10 || endIndex == -1) {
            throw new IOException("无榜单数据");
        }
        html = html.substring(beginIndex, endIndex);

        //封装成 shop 对象
        List<DZDPShop> shopList = new LinkedList<>();
        int begin = -1;
        int end = 0;
        while (begin < html.length()) {
            begin = html.indexOf("{", end);
            if (begin == -1) {
                break;
            }
            //应对评论里出现{}的情况
            int favIndex = html.indexOf("favorCount", begin);
            int firstRight = html.indexOf("}", favIndex) + 1;
            end = html.indexOf("}", firstRight) + 1;
            String substring = html.substring(begin, end);
            DZDPShop dzdpShop = JSON.parseObject(substring, DZDPShop.class);
            dzdpShop.setCityid(cityId);
            dzdpShop.setSkuid(skuId);
            shopList.add(dzdpShop);
        }
        shopList.forEach(o -> System.out.println(o));
        System.out.println("榜单店铺数：" + shopList.size());
        return shopList;
    }

    public void writeToDB(List<DZDPShop> list, Integer cityId, String cityName, Integer skuId, String skuName) {
        for (DZDPShop shop : list) {
            DZDPShopInfo shopInfo = shop.getShopinfo();
            DzdpRank rank = new DzdpRank();

            BeanUtil.copyProperties(shop, rank);
            BeanUtil.copyProperties(shopInfo, rank);

            rank.setFiveSub1Taste(shopInfo.getFiveSub1());
            rank.setFiveSub2Env(shopInfo.getFiveSub2());
            rank.setFiveSub3Serv(shopInfo.getFiveSub3());
            rank.setCityName(cityName);
            rank.setSkuName(skuName);
            String dishDesc = rank.getDishDesc();

            //替换掉表情包,双重过滤
            dishDesc = EmojiParser.parseToAliases(dishDesc);
            String characterFilter = "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]";
            dishDesc = dishDesc.replaceAll(characterFilter, "");
            rank.setDishDesc(dishDesc);

            rankMapper.insertSelective(rank);
        }
    }

}

